:- op(800, xfy, '&').   /* Conjunction */ 
:- op(850, xfy, 'v').   /* Disjunction */ 
:- op(870, xfy, '=>').  /* Implication */
:- op(880,xfy, '<=>').  /* Equivalence */
:- op(950, xfy, ':').   /* Action sequence */
:- op(960, xfy, '#').   /* Nondeterministic action choice */
:- op(880,fx, '?').  /* Equivalence */
 
do(E1 : E2,S,S1) :- do(E1,S,S2), do(E2,S2,S1).
do(?(P),S,S) :- holds(P,S).
do(E1 # E2,S,S1) :- do(E1,S,S1) ; do(E2,S,S1).
do(if(P,E1,E2),S,S1) :- do((?(P) : E1) # (?(-P) : E2),S,S1).
do(star(E),S,S1) :- S1 = S ; do(E : star(E),S,S1).
do(while(P,E),S,S1):- do(star(?(P) : E) : ?(-P),S,S1).
do(pi(V,E),S,S1) :- sub(V,_,E,E1), do(E1,S,S1).
do(E,S,S1) :- proc(E,E1), do(E1,S,S1).
do(E,S,do(E,S)) :- primitive_action(E), poss(E,S).

/* sub(Name,New,Term1,Term2): Term2 is Term1 with Name replaced by New. */

sub(X1,X2,T1,T2) :- var(T1), T2 = T1.
sub(X1,X2,T1,T2) :- not var(T1), T1 = X1, T2 = X2.
sub(X1,X2,T1,T2) :- not T1 = X1, T1 =..[F|L1], sub_list(X1,X2,L1,L2),
                    T2 =..[F|L2].
sub_list(X1,X2,[],[]).
sub_list(X1,X2,[T1|L1],[T2|L2]) :- sub(X1,X2,T1,T2), sub_list(X1,X2,L1,L2).

/* The holds predicate implements the revised Lloyd-Topor
   transformations on test conditions.  */

holds(P & Q,S) :- holds(P,S), holds(Q,S).
holds(P v Q,S) :- holds(P,S); holds(Q,S).
holds(P => Q,S) :- holds(-P v Q,S).
holds(P <=> Q,S) :- holds((P => Q) & (Q => P),S).
holds(-(-P),S) :- holds(P,S).
holds(-(P & Q),S) :- holds(-P v -Q,S).
holds(-(P v Q),S) :- holds(-P & -Q,S).
holds(-(P => Q),S) :- holds(-(-P v Q),S).
holds(-(P <=> Q),S) :- holds(-((P => Q) & (Q => P)),S).
holds(-all(V,P),S) :- holds(some(V,-P),S).
holds(-some(V,P),S) :- not holds(some(V,P),S).  /* Negation */
holds(-P,S) :- isAtom(P), not holds(P,S).     /* by failure */
holds(all(V,P),S) :- holds(-some(V,-P),S).
holds(some(V,P),S) :- sub(V,_,P,P1), holds(P1,S).

/* The following clause treats the holds predicate for non fluents, including
   Prolog system predicates. For this to work properly, the GOLOG programmer
   must provide, for all fluents, a clause giving the result of restoring
   situation arguments to situation-suppressed terms, for example:
         restoreSitArg(ontable(X),S,ontable(X,S)).             */

holds(A,S) :- restoreSitArg(A,S,F), F ;
              not restoreSitArg(A,S,F), isAtom(A), A.

isAtom(A) :- not (A = -W ; A = (W1 & W2) ; A = (W1 => W2) ;
    A = (W1 <=> W2) ; A = (W1 v W2) ; A = some(X,W) ; A = all(X,W)).

restoreSitArg(poss(A),S,poss(A,S)).

:- discontiguous(currentFloor/1).

:- discontiguous(currentFloor/2).

				   

% Primitive control actions



primitive_action(turnoff(_N)).  % Turn off call button N.

primitive_action(open).        % Open elevator door.

primitive_action(close).       % Close elevator door.

primitive_action(up(_N)).       % Move elevator up to floor N.

primitive_action(down(_N)).     % Move elevator down to floor N.



% Definitions of Complex Control Actions



proc(goFloor(N), ?(currentFloor(N)) # up(N) # down(N)).

proc(serve(N), goFloor(N) : turnoff(N) : open : close).

proc(serveAfloor, pi(n, ?(nextFloor(n)) : serve(n))).

proc(park, if(currentFloor(0), open, down(0) : open)).



%% control is the main loop. So long as there is an active call

%% button, it serves one floor. When all buttons are off, it

%% parks the elevator.



proc(control, while(some(n, on(n)), serveAfloor) : park).



% Preconditions for Primitive Actions.



poss(up(N),S) :- currentFloor(M,S), M < N.

poss(down(N),S) :- currentFloor(M,S), M > N.

poss(open,_S).

poss(close,_S).

poss(turnoff(N),S) :- on(N,S).



% Successor State Axioms for Primitive Fluents.



currentFloor(M,do(A,S)) :- A = up(M) ; A = down(M) ;

              \+ A = up(N), \+ A = down(N), currentFloor(M,S).



on(M,do(A,S)) :- on(M,S), \+ A = turnoff(M).



% Initial Situation. Call buttons: 3 and 5. The elevator is at floor 4.



on(3,s0).   on(5,s0).   currentFloor(4,s0).



%% nextFloor(N,S) is an abbreviation that determines which of the

%% active call buttons should be served next. Here, we simply

%% choose an arbitrary active call button.



nextFloor(N,S) :- on(N,S).



% Restore suppressed situation arguments.



%% These are just some auxiliary clauses that connect fluents with

%% situations.

restoreSitArg(on(N),S,on(N,S)).

restoreSitArg(nextFloor(N),S,nextFloor(N,S)).

restoreSitArg(currentFloor(M),S,currentFloor(M,S)).

yeq(X,Y):- X=Y.

signal(turnoff(N),S):-
	text_term(NS,N), text_concat('Turn-off button at floor ', NS, S).
signal(up(N),S):-
	text_term(NS,N), text_concat('Move up to floor ', NS, S).
signal(down(N),S):-
	text_term(NS,N), text_concat('Move down to floor ', NS, S).
signal(open,'Open door').
signal(close, 'Close door').

%% YAGI Signal handling
unwind(s0,[]).
unwind(do(A,s0),[S]):- signal(A,S).
unwind(do(A,Drec),L) :- unwind(Drec,Srec), signal(A,S), append(Srec,[S],L).